#include <bits/stdc++.h>

using namespace std;

int *Edge_Off_Mem_Array;
int *Edge_Mem_Array;
int *Degree_Array;
int Vertex_Num;
int Edge_Num;

void mem_init (const char *offset_filename, const char *edge_info_filename, const char *degree_filename) {
    FILE *p1, *p2, *p3;

    if ((p1 = fopen(offset_filename, "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << offset_filename << endl;
        exit(-1);
    }
    cout << "[INFO] Open file " << offset_filename << endl;

    if ((p2 = fopen(edge_info_filename, "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << edge_info_filename << endl;
        exit(-1);
    }
    cout << "[INFO] Open file " << edge_info_filename << endl;

    if ((p3 = fopen(degree_filename, "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << degree_filename << endl;
        exit(-1);
    }

    fscanf(p1, "%d", &Vertex_Num);
    fscanf(p2, "%d", &Edge_Num);

    Edge_Off_Mem_Array  = (int *) calloc (Vertex_Num + 1, sizeof(int));
    Edge_Mem_Array      = (int *) calloc (Edge_Num, sizeof(int));
    Degree_Array        = (int *) calloc (Edge_Num, sizeof(int));

    cout << "[INFO] Vertex_num = " << Vertex_Num << ", Edge_num = " << Edge_Num << endl;

    cout << "[INFO] Reading offset." << endl;
    for (int i = 0; i < Vertex_Num + 1; i ++) {
        fscanf(p1, "%d", &Edge_Off_Mem_Array[i]);
        if (i && (Edge_Off_Mem_Array[i] == Edge_Off_Mem_Array[i - 1])) {
            cout << "[ERROR] Invalid edge offset : Vertex_id = " << i << ", Off_l = " << Edge_Off_Mem_Array[i - 1] << ", Off_r = " << Edge_Off_Mem_Array[i] << endl;
            exit(-1);
        }
    }
    cout << "[INFO] Reading offset complete." << endl;

    cout << "[INFO] Reading edge list." << endl;
    for (int i = 0; i < Edge_Num; i ++) {
        fscanf(p2, "%d", &Edge_Mem_Array[i]);
    }
    cout << "[INFO] Reading edge list complete." << endl;

    cout << "[INFO] Reading degree list." << endl;
    for (int i = 0; i < Edge_Num; i ++) {
        fscanf(p3, "%d", &Degree_Array[i]);
    }
    cout << "[INFO] Reading degree list complete." << endl;

    fclose(p1);
    fclose(p2);
    fclose(p3);
}

void k_channel_trans (const char *offset_filename, const char *info_filename, const char *degree_filename, const char *result_folder, int channel) {
    FILE **offset_k_cn_fp, **info_k_cn_fp, **degree_k_cn_fp;
    char fname[200];

    offset_k_cn_fp = (FILE **) calloc (channel, sizeof(FILE *));
    info_k_cn_fp = (FILE **) calloc (channel, sizeof(FILE *));
    degree_k_cn_fp = (FILE **) calloc (channel, sizeof(FILE *));

    mem_init(offset_filename, info_filename, degree_filename);

    cout << "[INFO] Create k channel offset file." << endl;
    for (int i = 0; i < channel; i ++) {
        sprintf(fname, "%s/off_%d.txt", result_folder, i + 1);
        if ((offset_k_cn_fp[i] = fopen(fname, "w")) == NULL) {
            cout << "[ERROR] Failed to create k channel offest file " << fname << endl;
            exit (-1);
        }
    }
    cout << "[INFO] Create k channel offset file complete." << endl;

    cout << "[INFO] Create k channel info file." << endl;
    for (int i = 0; i < channel; i ++) {
        sprintf(fname, "%s/info_%d.txt", result_folder, i + 1);
        if ((info_k_cn_fp[i] = fopen(fname, "w")) == NULL) {
            cout << "[ERROR] Failed to create k channel info file " << fname << endl;
            exit (-1);
        }
    }
    cout << "[INFO] Create k channel info file complete." << endl;

    cout << "[INFO] Create k channel degree file." << endl;
    for (int i = 0; i < channel; i ++) {
        sprintf(fname, "%s/degree_%d.txt", result_folder, i + 1);
        if ((degree_k_cn_fp[i] = fopen(fname, "w")) == NULL) {
            cout << "[ERROR] Failed to create k channel degree file " << fname << endl;
            exit (-1);
        }
    }
    cout << "[INFO] Create k channel degree file complete." << endl;

    // 需要保证分割文件的 offset 数相等
    for (int i = 0; i < Vertex_Num + 1; i += channel * 16) {
        for (int j = 0; j < channel * 16; j ++) {
            if (i + j <= Vertex_Num) {
                fprintf(offset_k_cn_fp[j / 16], "%d\n", Edge_Off_Mem_Array[i + j]);
            } else {
                fprintf(offset_k_cn_fp[j / 16], "%d\n", 0);
            }
        }
    }

    // write info coe
    for (int i = 0; i < Edge_Num; i += channel * 16) {
        for (int j = 0; j < channel * 16; j ++) {
            if (i + j < Edge_Num) {
                fprintf(info_k_cn_fp[j / 16], "%d\n", Edge_Mem_Array[i + j]);
            } else {
                fprintf(info_k_cn_fp[j / 16], "%d\n", 0);
            }
        }
    }

    // write degree
    for (int i = 0; i < Edge_Num; i += channel * 16) {
        for (int j = 0; j < channel * 16; j ++) {
            if (i + j < Edge_Num) {
                fprintf(degree_k_cn_fp[j / 16], "%d\n", Degree_Array[i + j]);
            } else {
                fprintf(degree_k_cn_fp[j / 16], "%d\n", 0);
            }
        }
    }

    for (int i = 0; i < channel; i ++) {
        fclose(offset_k_cn_fp[i]);
        fclose(info_k_cn_fp[i]);
        fclose(degree_k_cn_fp[i]);
    }

    cout << "[INFO] Generate channel file complete." << endl;

}

int main(int argc, char **argv) {
    int channel;

    if (argc != 6) {
        cout << "[INFO] Parameter (offset_file, info_file, degree_file, result_folder, channel_num)." << endl;
        exit (-1);
    }
    channel = atoi (argv[5]);
    cout << "[INFO] Set channel to " << channel << endl;

    k_channel_trans (argv[1], argv[2], argv[3], argv[4], channel);

}